#include "StdAfx.h"

#include "Directory.h"

#include "Common/Defs.h"

#include "Common/7Zip/PropID.h"

#include "Common/Agent/IFolderArchive.h"
#include "Common/Archive/CompressCall.h"

#include "Common/FileSystem/ArchiveName.h"

#include "Common/FileSystem/FileFind.h"
#include "Common/FileSystem/FileName.h"
#include "Common/FileSystem/Wildcard.h"

#include "Common/Lang/LangUtils.h"

#include "Common/MS/PropVariant.h"

#include "Common/Props/PropIDUtils.h"

#include "Common/String/FormatUtils.h"
#include "Common/String/DataConversions.h"
#include "Common/System/Defs.h"

#include "PropertyName.h"

#include "../../resource.h"
#include "../../ExtractCallback.h"
#include "../../CopyDialog.h"

#include "../Windows.h"

#include <algorithm>



namespace FileManager
{
namespace FileSystem
{
	static LPCTSTR kSeparator = _T("----------------------------\n");
	static LPCTSTR kSeparatorSmall = _T("----\n");
	static LPCTSTR kPropValueSeparator = _T(": ");

	//moved from Panel.cpp

	void CDirectory::ChangeFlatMode()
	{
		_flatMode = !_flatMode;
		if (_parentFolders.size() > 0)
		{
			_flatModeForArc = _flatMode;
		}
		else
		{
			_flatModeForDisk = _flatMode;
		}
		CComPtr<IFolderSetFlatMode> folderSetFlatMode;
		_folder.QueryInterface(&folderSetFlatMode);
		if (folderSetFlatMode)
		{
			folderSetFlatMode->SetFlatMode(BoolToInt(_flatMode));
		}
	}

	static CString GetSubFolderNameForExtract(const CString &archiveName)
	{
		int slashPos = archiveName.ReverseFind(CHAR_PATH_SEPARATOR);
		int dotPos = archiveName.ReverseFind(_T('.'));
		if (dotPos < 0 || slashPos > dotPos)
		{
			return archiveName + CString(_T("~"));
		}
		CString res = archiveName.Left(dotPos);
		res.TrimRight();
		return res;
	}

	bool CDirectory::ExtractArchives(std::vector<UINT32> &indices)
	{
		CStringVector paths;
		GetFilePaths(indices, paths);
		if (paths.empty())
		{
			return false;
		}
		CString folderName;
		if (indices.size() == 1)
		{
			folderName = GetSubFolderNameForExtract(GetItemRelPath(indices[0]));
		}
		else
		{
			folderName = _T("*");
		}
		return S_OK == Common::Archive::ExtractArchives(paths, GetCurrentDirectory() + folderName + CString(CHAR_PATH_SEPARATOR), true);
	}

	void CDirectory::AddToArchive(std::vector<UINT32> &indices)
	{
		if (!IsFsOrDrivesFolder())
		{
			CWindows::MessageBoxErrorLang(m_hWnd, IDS_OPERATION_IS_NOT_SUPPORTED, 0x03020208);
			return;
		}
		if (indices.size() == 0)
		{
			CWindows::MessageBoxErrorLang(m_hWnd, IDS_SELECT_FILES, 0x03020A03);
			return;
		}
		CStringVector names;

		CString curPrefix = GetCurrentDirectory();
		CString destCurDirPrefix(curPrefix);
		if (IsFSDrivesFolder())
		{
			destCurDirPrefix = ROOT_FS_FOLDER;
			if (!IsDeviceDrivesPrefix())
			{
				curPrefix.Empty();
			}
		}

		for (size_t i = 0; i < indices.size(); i++)
		{
			names.push_back(curPrefix + GetItemRelPath(indices[i]));
		}
		const CString archiveName = Common::FileSystem::CreateArchiveName(names.front(), (names.size() > 1), false);
		HRESULT res = Common::Archive::CompressFiles(destCurDirPrefix, archiveName, _T(""), names, false, true, false);
		if (res != S_OK)
		{
			if (destCurDirPrefix.GetLength() >= MAX_PATH)
			{
				FileManager::CWindows::MessageBoxErrorLang(m_hWnd, IDS_MESSAGE_UNSUPPORTED_OPERATION_FOR_LONG_PATH_FOLDER, 0x03020A01);
			}
		}

	}

	static void AddValuePair(UINT resourceID, UINT32 langID, UINT64 value, CString &s)
	{
		TCHAR sz[32];
		s += Common::Lang::LangString(resourceID, langID);
		s += _T(' ');
		_ui64tot(value, sz, 10);
		s += sz;
		s += _T('\n');
	}

	static CString AddSizeValue(UINT64 size)
	{
		return Common::String::PatternFormat(IDS_FILE_SIZE, 0x02000982, Common::String::ConvertSizeToString(size));
	}

	static void AddValuePair1(UINT resourceID, UINT32 langID, UINT64 size, CString &s)
	{
		s += Common::Lang::LangString(resourceID, langID);
		s += _T(" ");
		s += AddSizeValue(size);
		s += _T("\n");
	}


	void AddValuePair2(UINT resourceID, UINT32 langID, UINT64 num, UINT64 size, CString &s)
	{
		if (num == 0)
		{
			return;
		}
		s += Common::Lang::LangString(resourceID, langID);
		s += _T(" ");
		s += Common::String::ConvertSizeToString(num);

		if (size != (UINT64)(INT64)-1)
		{
			s += _T("    ( ");
			s += AddSizeValue(size);
			s += _T(" )");
		}
		s += _T("\n");
	}



	static void AddPropValueToSum(IFolderFolder* folder, int index, PROPID propID, UINT64 &sum)
	{
		if (sum == (UINT64)(INT64)-1)
		{
			return;
		}
		CComPropVariant prop;
		folder->GetProperty(index, propID, &prop);
		switch (prop.vt)
		{
		case VT_UI4:
		case VT_UI8:
			sum += Common::String::ConvertPropVariantToUInt64(prop);
			break;
		default:
			sum = (UINT64)(INT64)-1;
		}
	}

	CString CDirectory::GetItemsInfoString(const std::vector<UINT32> &indices)
	{
		CString info;
		UINT64 numDirs, numFiles, filesSize, foldersSize;
		numDirs = numFiles = filesSize = foldersSize = 0;
		size_t i;
		for (i = 0; i < indices.size(); i++)
		{
			int index = indices[i];
			if (IsItemFolder(index))
			{
				AddPropValueToSum(_folder, index, kpidSize, foldersSize);
				numDirs++;
			}
			else
			{
				AddPropValueToSum(_folder, index, kpidSize, filesSize);
				numFiles++;
			}
		}

		AddValuePair2(IDS_FOLDERS_COLON, 0x02000321, numDirs, foldersSize, info);
		AddValuePair2(IDS_FILES_COLON, 0x02000320, numFiles, filesSize, info);
		int numDefined = ((foldersSize != (UINT64)(INT64)-1) && foldersSize != 0) ? 1 : 0;
		numDefined += ((filesSize != (UINT64)(INT64)-1) && filesSize != 0) ? 1 : 0;
		if (numDefined == 2)
		{
			AddValuePair1(IDS_SIZE_COLON, 0x02000322, filesSize + foldersSize, info);
		}

		info += _T("\n");
		info += GetCurrentDirectory();

		for (i = 0; i < indices.size() && i < kCopyDialog_NumInfoLines - 6; i++)
		{
			info += _T("\n  ");
			int index = indices[i];
			info += GetItemRelPath(index);
			if (IsItemFolder(index))
			{
				info += CHAR_PATH_SEPARATOR;
			}
		}
		if (i != indices.size())
		{
			info += _T("\n  ...");
		}
		return info;
	}


	class CThreadTest : public CProgressThreadVirt
	{
		HRESULT ProcessVirt();
	public:
		std::vector<UINT32> Indices;
		CExtractCallbackImp* ExtractCallbackSpec;
		CComPtr<IFolderArchiveExtractCallback> ExtractCallback;
		CComPtr<Common::Agent::IArchiveFolder> ArchiveFolder;
		CThreadTest(CProgressThreadVirt::ThreadPoolType* pool) : CProgressThreadVirt(pool)
		{}
	};

	HRESULT CThreadTest::ProcessVirt()
	{
		HRESULT hRes(ArchiveFolder->Extract(&Indices[0], Indices.size(),
			Common::Archive::Extract::PathMode::kFullPathnames,
			Common::Archive::Extract::OverwriteMode::kAskBefore,
			NULL, BoolToInt(true), ExtractCallback));
		if (S_OK != hRes)
		{
			return hRes;
		}
		if (ExtractCallbackSpec->IsOK())
		{
			CString s;
			AddValuePair(IDS_FOLDERS_COLON, 0x02000321, ExtractCallbackSpec->NumFolders, s);
			AddValuePair(IDS_FILES_COLON, 0x02000320, ExtractCallbackSpec->NumFiles, s);
			s += _T('\n');
			s += Common::Lang::LangString(IDS_MESSAGE_NO_ERRORS, 0x02000608);
			OkMessage = s;
		}
		return S_OK;
	}

	bool CDirectory::TestArchives(std::vector<UINT32> &indices)
	{
		if (indices.empty())
		{
			return false;
		}
		CComPtr<Common::Agent::IArchiveFolder> archiveFolder;
		_folder->QueryInterface(&archiveFolder);
		if (archiveFolder)
		{
			{
				CThreadTest extracter(&m_pool);

				extracter.ArchiveFolder = archiveFolder;
				extracter.ExtractCallbackSpec = new CComObject<CExtractCallbackImp>;
				extracter.ExtractCallbackSpec->FinalConstruct();
				extracter.ExtractCallback = extracter.ExtractCallbackSpec;
				extracter.ExtractCallbackSpec->ProgressDialog = &extracter.ProgressDialog;

				extracter.Indices.assign(indices.begin(), indices.end());

				CString title = Common::Lang::LangString(IDS_PROGRESS_TESTING, 0x02000F90);
				CString progressWindowTitle = Common::Lang::LangString(IDS_APP_TITLE, 0x03000000);

				extracter.ProgressDialog.CompressingMode = false;
				extracter.ProgressDialog.MainWindow = m_hWnd;
				extracter.ProgressDialog.MainTitle = progressWindowTitle;
				extracter.ProgressDialog.MainAddTitle = title + _T(" ");

				extracter.ExtractCallbackSpec->OverwriteMode = Common::Archive::Extract::OverwriteMode::kAskBefore;
				extracter.ExtractCallbackSpec->Init();

				if (extracter.Create(title, m_hWnd) != S_OK)
				{
					return false;
				}

			}			
			return true;
		}

		if (!IsFSFolder())
		{
			CWindows::MessageBoxErrorLang(m_hWnd, IDS_OPERATION_IS_NOT_SUPPORTED, 0x03020208);
			return false;
		}
		CStringVector paths;
		GetFilePaths(indices, paths);
		if (paths.empty())
		{
			return false;
		}
		return S_OK == Common::Archive::TestArchives(paths);
	}
	
	void CDirectory::GetFilePaths(const std::vector<UINT32> &indices, CStringVector &paths) const
	{
		for (size_t i = 0; i < indices.size(); i++)
		{
			int index = indices[i];
			if (IsItemFolder(index))
			{
				paths.clear();
				break;
			}
			paths.push_back(GetItemFullPath(index));
		}
		if (paths.size() == 0)
		{
			FileManager::CWindows::MessageBoxErrorLang(m_hWnd, IDS_SELECT_FILES, 0x03020A03);
			return;
		}
	}


	//moved from PanelItems.cpp

	void CDirectory::ResetProperties(CListViewInfo & listInfo)
	{
		_properties.clear();
		UINT32 numProperties;
		_folder->GetNumberOfProperties(&numProperties);
		_properties.reserve(numProperties);
		
		HRESULT hRes(S_OK);
		for (UINT32 i = 0; i < numProperties; i++)
		{
			CComBSTR name;
			PROPID propID;
			VARTYPE varType;

			hRes = _folder->GetPropertyInfo(i, &name, &propID, &varType);
			if (S_OK != hRes)
			{
				return;
			}

			if (propID == kpidIsDir)
			{
				continue;
			}

			CItemProperty prop;
			prop.Type = varType;
			prop.ID = propID;
			prop.Name = GetNameOfProperty(propID, name);
			prop.Order = -1;
			prop.IsVisible = true;
			prop.Width = 100;
			_properties.push_back(prop);		
		}

		int order (0);
		CListViewInfo::ColumnsType::size_type i(0);
		for (; i < listInfo.Columns.size(); i++)
		{
			const auto &columnInfo = listInfo.Columns[i];
			int index = _properties.FindItemWithID(columnInfo.PropID);
			if (index >= 0)
			{
				auto &item = _properties[index];
				item.IsVisible = columnInfo.IsVisible;
				item.Width = columnInfo.Width;
				if (columnInfo.IsVisible)
				{
					item.Order = order++;
				}
				continue;
			}
		}
		for (auto &item : _properties)
		{
			if (item.Order < 0)
			{
				item.Order = order++;
			}
		}

		_visibleProperties.clear();
		_visibleProperties.reserve(numProperties);
		for (size_t i = 0; i < _properties.size(); i++)
		{
			const CItemProperty &prop = _properties[i];
			if (prop.IsVisible)
			{
				_visibleProperties.push_back(prop);
			}
		}
		std::sort(_visibleProperties.begin(), _visibleProperties.end());
	}

	CString CDirectory::GetItemName(int itemIndex) const
	{
		if (itemIndex == kParentIndex)
		{
			return _T("..");
		}
		CComPropVariant prop;
		if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK)
		{
			throw 2723400;
		}
		if (!(VT_BSTR == prop.vt || VT_LPWSTR == prop.vt))
		{
			throw 2723401;
		}
		return prop.bstrVal;
	}

	CString CDirectory::GetItemExtension(int itemIndex) const
	{
		if (itemIndex == kParentIndex)
		{
			return _T("..");
		}
		CComPropVariant prop;
		if (_folder->GetProperty(itemIndex, kpidExtension, &prop) != S_OK)
		{
			throw 2723400;
		}
		if (!(VT_BSTR == prop.vt || VT_LPWSTR == prop.vt))
		{
			throw 2723401;
		}
		return prop.bstrVal;
	}


	CString CDirectory::GetItemPrefix(int itemIndex) const
	{
		if (itemIndex == kParentIndex)
		{
			return CString();
		}
		CComPropVariant prop;
		if (_folder->GetProperty(itemIndex, kpidPrefix, &prop) != S_OK)
		{
			throw 2723400;
		}
		CString prefix;
		if (prop.vt == VT_BSTR)
		{
			prefix = prop.bstrVal;
		}
		else if (VT_LPWSTR == prop.vt)
		{
			prefix = prop.pwszVal;
		}
		return prefix;
	}

	CString CDirectory::GetItemRelPath(int itemIndex) const
	{
		return GetItemPrefix(itemIndex) + GetItemName(itemIndex);
	}

	CString CDirectory::GetItemFullPath(int itemIndex) const
	{
		return GetCurrentDirectory() + GetItemRelPath(itemIndex);
	}

	bool CDirectory::IsItemFolder(int itemIndex) const
	{
		if (itemIndex == kParentIndex)
		{
			return true;
		}
		CComPropVariant prop;
		if (_folder->GetProperty(itemIndex, kpidIsDir, &prop) != S_OK)
		{
			throw 2723400;
		}
		if (prop.vt == VT_BOOL)
		{
			return VARIANT_BOOLToBool(prop.boolVal);
		}
		if (prop.vt == VT_EMPTY)
		{
			return false;
		}
		return false;
	}

	UINT64 CDirectory::GetItemSize(int itemIndex) const
	{
		if (itemIndex == kParentIndex)
		{
			return 0;
		}
		CComPropVariant prop;
		if (_folder->GetProperty(itemIndex, kpidSize, &prop) != S_OK)
		{
			throw 2723400;
		}
		if (prop.vt == VT_EMPTY)
		{
			return 0;
		}
		return Common::String::ConvertPropVariantToUInt64(prop);
	}


	//Moved from Panel.cpp
	CString CDirectory::GetFolderTypeID() const
	{
		CComPropVariant prop;
		if (_folder->GetFolderProperty(kpidType, &prop) == S_OK)
		if (prop.vt == VT_BSTR)
		{
			return (const wchar_t*)prop.bstrVal;
		}
		return _T("");
	}

	bool CDirectory::IsFolderTypeEqTo(LPCTSTR s) const
	{
		return GetFolderTypeID() == s;
	}

	bool CDirectory::IsRootFolder() const
	{
		return IsFolderTypeEqTo(_T("RootFolder"));
	}
	bool CDirectory::IsFSFolder() const
	{
		return IsFolderTypeEqTo(_T("FSFolder"));
	}
	bool CDirectory::IsFSDrivesFolder() const
	{
		return IsFolderTypeEqTo(_T("FSDrives"));
	}
	bool CDirectory::IsArcFolder() const
	{
		CString s = GetFolderTypeID();
		return s.Left(5) == _T("7-Zip");
	}

	CString CDirectory::GetFsPath() const
	{
		if (IsFSDrivesFolder() && !IsDeviceDrivesPrefix())
		{
			return CString();
		}
		return GetCurrentDirectory();
	}

	CString CDirectory::GetDriveOrNetworkPrefix() const
	{
		if (!IsFSFolder())
		{
			return CString();
		}
		CString drive = GetFsPath();
		if (drive.GetLength() < 3)
		{
			return CString();
		}
		if (drive[0] == _T('\\') && drive[1] == _T('\\'))
		{
			// if network
			int pos = drive.Find(_T('\\'), 2);
			if (pos < 0)
			{
				return CString();
			}
			pos = drive.Find(_T('\\'), pos + 1);
			if (pos < 0)
			{
				return CString();
			}
			return drive.Left(pos + 1);
		}
		if (drive[1] != _T(':') || drive[2] != _T('\\'))
		{
			return CString();
		}
		return drive.Left(3);
	}

	bool CDirectory::DoesItSupportOperations() const
	{
		CComPtr<IFolderOperations> folderOperations;
		return _folder.QueryInterface(&folderOperations) == S_OK;
	}

	//Taken from PanelSelect.cpp
	void CDirectory::SelectByType(int index, std::vector<bool>& selection, bool selectMode)
	{
		if (kParentIndex == index)
		{
			return;
		}
		
		CString name = GetItemName(index);
		bool isItemFolder = IsItemFolder(index);

		if (isItemFolder)
		{
			for (size_t i = 0; i < selection.size(); i++)
			if (IsItemFolder(i) == isItemFolder)
			{
				selection[i] = selectMode;
			}
		}
		else
		{
			int pos = name.ReverseFind(_T('.'));
			if (pos < 0)
			{
				for (size_t i = 0; i < selection.size(); i++)
				if (IsItemFolder(i) == isItemFolder && GetItemName(i).ReverseFind(_T('.')) < 0)
				{
					selection[i] = selectMode;
				}
			}
			else
			{
				CString mask = CString(_T('*')) + name.Mid(pos);
				for (size_t i = 0; i < selection.size(); i++)
				if (IsItemFolder(i) == isItemFolder && Common::FileSystem::CompareWildCardWithName(mask, GetItemName(i)))
				{
					selection[i] = selectMode;
				}
			}
		}
	}

	
	//taken from PanelMenu.cpp

	static void AddPropertyString(PROPID propID, const wchar_t* nameBSTR,
		const CComPropVariant &prop, CString &s)
	{
		if (prop.vt != VT_EMPTY)
		{
			CString val;

			if ((prop.vt == VT_UI8 || prop.vt == VT_UI4) && (
				propID == kpidSize ||
				propID == kpidPackSize ||
				propID == kpidNumSubDirs ||
				propID == kpidNumSubFiles ||
				propID == kpidNumBlocks ||
				propID == kpidClusterSize ||
				propID == kpidTotalSize ||
				propID == kpidFreeSpace ||
				propID == kpidPhySize ||
				propID == kpidHeadersSize
				))
			{
				val = Common::String::ConvertSizeToString(Common::String::ConvertPropVariantToUInt64(prop));
			}
			else
			{
				val = Common::Props::ConvertPropertyToString(prop, propID);
			}

			if (!val.IsEmpty())
			{
				s += GetNameOfProperty(propID, nameBSTR);
				s += kPropValueSeparator;
				s += val;
				s += _T('\n');
			}
		}
	}

	HRESULT CDirectory::CreateShellContextMenu(const std::vector<UINT32> &operatedIndices,
		CComPtr<IContextMenu> &systemContextMenu) const
	{
		systemContextMenu.Release();
		CString folderPath = GetFsPath();

		CComPtr<IShellFolder> desktopFolder;
		HRESULT hRes(::SHGetDesktopFolder(&desktopFolder));
		if (S_OK != hRes)
		{
			return hRes;
		}
		if (!desktopFolder)
		{
			return E_FAIL;
		}

		// Separate the file from the folder.


		// Get a pidl for the folder the file
		// is located in.
		LPITEMIDLIST parentPidl;
		DWORD eaten;
		hRes = desktopFolder->ParseDisplayName(m_hWnd, 0, (wchar_t*)(const wchar_t*)folderPath,
			&eaten, &parentPidl, 0);
		if (S_OK != hRes)
		{
			return hRes;
		}

		// Get an IShellFolder for the folder
		// the file is located in.
		CComPtr<IShellFolder> parentFolder;
		hRes = desktopFolder->BindToObject(parentPidl, 0, IID_IShellFolder, (void**)&parentFolder);
		if (S_OK != hRes)
		{
			return hRes;
		}
		if (!parentFolder)
		{
			return E_FAIL;
		}

		// Get a pidl for the file itself.
		std::vector<LPITEMIDLIST> pidls;
		pidls.reserve(operatedIndices.size());
		for (size_t i = 0; i < operatedIndices.size(); i++)
		{
			LPITEMIDLIST pidl;
			CString fileName = GetItemRelPath(operatedIndices[i]);
			if (IsFSDrivesFolder())
			{
				fileName += CHAR_PATH_SEPARATOR;
			}
			hRes = parentFolder->ParseDisplayName(m_hWnd, 0,
				(wchar_t*)(const wchar_t*)fileName, &eaten, &pidl, 0);
			if (S_OK != hRes)
			{
				return hRes;
			}
			pidls.push_back(pidl);
		}

		ITEMIDLIST temp;
		if (pidls.size() == 0)
		{
			temp.mkid.cb = 0;
			pidls.push_back(&temp);
		}

		// Get the IContextMenu for the file.
		CComPtr<IContextMenu> cm;
		hRes = parentFolder->GetUIObjectOf(m_hWnd, pidls.size(),
			(LPCITEMIDLIST*)&pidls.front(), IID_IContextMenu, 0, (void**)&cm);
		if (S_OK != hRes)
		{
			return hRes;
		}
		if (!cm)
		{
			return E_FAIL;
		}
		systemContextMenu = cm;
		return S_OK;
	}


	void CDirectory::InvokeSystemCommand(const char* command, const std::vector<UINT32> &operatedIndices) const
	{
		if (!IsFsOrPureDrivesFolder())
		{
			return;
		}
		if (operatedIndices.empty())
		{
			return;
		}
		CComPtr<IContextMenu> contextMenu;
		if (CreateShellContextMenu(operatedIndices, contextMenu) != S_OK)
		{
			return;
		}

		CMINVOKECOMMANDINFO ci;
		ZeroMemory(&ci, sizeof(ci));
		ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
		ci.hwnd = m_hWnd;
		ci.lpVerb = command;
		contextMenu->InvokeCommand(&ci);
	}


	void CDirectory::GetProperties(const std::vector<UINT32> &operatedIndices) const
	{
		CComPtr<IGetFolderArcProps> getFolderArcProps;
		_folder.QueryInterface(&getFolderArcProps);
		if (!getFolderArcProps)
		{
			InvokeSystemCommand("properties", operatedIndices);
			return;
		}

		{
			CString message;

			if (operatedIndices.size() == 1)
			{
				UINT32 index = operatedIndices[0];
				// message += _T("Item:\n");
				UINT32 numProps;
				if (_folder->GetNumberOfProperties(&numProps) == S_OK)
				{
					for (UINT32 i = 0; i < numProps; i++)
					{
						CComBSTR name;
						PROPID propID;
						VARTYPE varType;

						if (_folder->GetPropertyInfo(i, &name, &propID, &varType) != S_OK)
						{
							continue;
						}

						CComPropVariant prop;
						if (_folder->GetProperty(index, propID, &prop) != S_OK)
						{
							continue;
						}
						AddPropertyString(propID, name, prop, message);
					}
				}
				message += kSeparator;
			}

			/*
			message += LangString(IDS_PROP_FILE_TYPE, 0x02000214);
			message += kPropValueSeparator;
			message += GetFolderTypeID();
			message += _T("\n");
			*/

			{
				CComPropVariant prop;
				if (_folder->GetFolderProperty(kpidPath, &prop) == S_OK)
				{
					AddPropertyString(kpidName, _T("Path"), prop, message);
				}
			}

			CComPtr<IFolderProperties> folderProperties;
			_folder->QueryInterface(__uuidof(IFolderProperties), (void**)&folderProperties);
			if (folderProperties)
			{
				UINT32 numProps;
				if (folderProperties->GetNumberOfFolderProperties(&numProps) == S_OK)
				{
					for (UINT32 i = 0; i < numProps; i++)
					{
						CComBSTR name;
						PROPID propID;
						VARTYPE vt;
						if (folderProperties->GetFolderPropertyInfo(i, &name, &propID, &vt) != S_OK)
						{
							continue;
						}
						CComPropVariant prop;
						if (_folder->GetFolderProperty(propID, &prop) != S_OK)
						{
							continue;
						}
						AddPropertyString(propID, name, prop, message);
					}
				}
			}

			CComPtr<IGetFolderArcProps> getFolderArcProps;
			_folder->QueryInterface(__uuidof(IGetFolderArcProps), (void**)&getFolderArcProps);
			if (getFolderArcProps)
			{
				CComPtr<IFolderArcProps> getProps;
				getFolderArcProps->GetFolderArcProps(&getProps);
				if (getProps)
				{
					UINT32 numLevels;
					if (getProps->GetArcNumLevels(&numLevels) != S_OK)
					{
						numLevels = 0;
					}
					for (UINT32 level2 = 0; level2 < numLevels; level2++)
					{
						{
							UINT32 level = numLevels - 1 - level2;
							UINT32 numProps;
							if (getProps->GetArcNumProps(level, &numProps) == S_OK)
							{
								message += kSeparator;
								for (INT32 i = -3; i < (INT32)numProps; i++)
								{
									CComBSTR name;
									PROPID propID;
									VARTYPE vt;
									switch (i)
									{
									case -3:
										propID = kpidPath;
										break;
									case -2:
										propID = kpidType;
										break;
									case -1:
										propID = kpidError;
										break;
									default:
										if (getProps->GetArcPropInfo(level, i, &name, &propID, &vt) != S_OK)
										{
											continue;
										}
									}
									CComPropVariant prop;
									if (getProps->GetArcProp(level, propID, &prop) != S_OK)
									{
										continue;
									}
									AddPropertyString(propID, name, prop, message);
								}
							}
						}
						if (level2 != numLevels - 1)
						{
							UINT32 level = numLevels - 1 - level2;
							UINT32 numProps;
							if (getProps->GetArcNumProps2(level, &numProps) == S_OK)
							{
								message += kSeparatorSmall;
								for (INT32 i = 0; i < (INT32)numProps; i++)
								{
									CComBSTR name;
									PROPID propID;
									VARTYPE vt;
									if (getProps->GetArcPropInfo2(level, i, &name, &propID, &vt) != S_OK)
									{
										continue;
									}
									CComPropVariant prop;
									if (getProps->GetArcProp2(level, propID, &prop) != S_OK)
									{
										continue;
									}
									AddPropertyString(propID, name, prop, message);
								}
							}
						}
					}
				}
			}
			::MessageBoxW(m_hWnd, message, Common::Lang::LangString(IDS_PROPERTIES, 0x03020900), MB_OK);
		}
	}

	//moved from App.cpp
	// reduces path to part that exists on disk
	void ReducePathToRealFileSystemPath(CString &path)
	{
		while (!path.IsEmpty())
		{
			if (Common::FileSystem::Find::DoesDirExist(path))
			{
				Common::FileSystem::FileName::NormalizeDirPathPrefix(path);
				break;
			}
			int pos = path.ReverseFind(CHAR_PATH_SEPARATOR);
			if (pos < 0)
			{
				path.Empty();
			}
			else
			{
				path = path.Left(pos + 1);
				if (path.GetLength() == 3 && path[1] == _T(':'))
				{
					break;
				}
				if (path.GetLength() > 2 && path[0] == _T('\\') && path[1] == _T('\\'))
				{
					int nextPos = path.Find(CHAR_PATH_SEPARATOR, 2); // pos after \\COMPNAME
					if (nextPos > 0 && path.Find(CHAR_PATH_SEPARATOR, nextPos + 1) == pos)
					{
						break;
					}
				}
				path = path.Left(pos);
			}
		}
	}

	// return true for dir\, if dir exist
	bool CheckFolderPath(const CString &path)
	{
		CString pathReduced = path;
		ReducePathToRealFileSystemPath(pathReduced);
		return (pathReduced == path);
	}


	bool IsPathAbsolute(const CString &path)
	{
		if (path.GetLength() >= 1 && path[0] == CHAR_PATH_SEPARATOR)
		{
			return true;
		}
#ifdef _WIN32
		if (path.GetLength() >= 3 && path[1] == _T(':') && path[2] == _T('\\'))
		{
			return true;
		}
#endif
		return false;
	}

	
	bool CDirectory::FolderHasChanged() const
	{
		CComPtr<IFolderWasChanged> folderWasChanged;
		if (_folder->QueryInterface(&folderWasChanged) != S_OK)
		{
			return false;
		}
		INT32 wasChanged;
		if (folderWasChanged->WasChanged(&wasChanged) != S_OK)
		{
			return false;
		}
		if (wasChanged == 0)
		{
			return false;
		}
		return true;
	}

} //namespace FileSystem
} //namespace FileManager